package optimizations.parser;

import java.util.EnumMap;
import java.util.Iterator;
import java.util.Map;

import java_cup.runtime.ComplexSymbolFactory;
import java_cup.runtime.Scanner;
import java_cup.runtime.Symbol;
import java_cup.runtime.ComplexSymbolFactory.ComplexSymbol;
import optimizations.lexer.Lexer;
import optimizations.lexer.Token;
import optimizations.lexer.TokenType;

/**
 * An adapter that allows {@link Lexer} instances to interface as CUP
 * {@linkplain Scanner}s.
 *
 * <p>This class adapts a {@linkplain Lexer} to produce
 * {@linkplain ComplexSymbol} instances for CUP. As such, this
 * {@linkplain Scanner} works with CUP's provided {@link ComplexSymbolFactory}
 * symbol management tool, which is used to construct nonterminal symbols every
 * time it carries out reductions. The {@linkplain ComplexSymbolFactory} handles
 * position tracking for your parsed entities. See the CUP manual for an example
 * of how to take advantage of this.
 *
 * <p>The {@linkplain Symbol#value} of the returned {@linkplain Symbol}
 * instances&mdash;the value of the terminals in your CUP specification&mdash;is
 * configurable. By default, these values are the {@linkplain Token}
 * instances produced by the given lexer.  This behavior may be changed by
 * extending this class and overriding {@link #valueOf(Token)}.
 *
 * @see <a
 *      href="http://www2.cs.tum.edu/projects/cup/manual.html#advanced-symbols">
 *      CUP manual section on symbol management</a>
 */
public class LexerAdapter implements Scanner {
  private static final String EOF = "EOF";

  private final Iterator<Token> lexer;
  private final Map<TokenType, Integer> tokenTypes;
  private final int eof;

  /**
   * Constructs an adapter for the given lexer and CUP-generated symbol class.
   *
   * <p>Call it like this:
   *
   * <pre><code>Scanner cupScanner = new LexerAdapter(myLexer, sym.class);
   * </code></pre>
   *
   * <p>The given class (named {@code sym} by default) must be generated by CUP
   * and include a terminal declaration for every {@linkplain TokenType}.
   *
   * @param lexer
   *          a Xi lexer
   * @param cupSymClass
   *          a CUP-generated symbol class satisfying the above constraints
   */
  public LexerAdapter(Lexer lexer, Class<?> cupSymClass) {
    this(lexer, new TokenTypeAdapter(cupSymClass));
  }

  private LexerAdapter(Lexer lexer, TokenTypeAdapter tokenTypeAdapter) {
    this(lexer, tokenTypeAdapter.tokenTypes(), tokenTypeAdapter.eof());
  }

  // Visible for testing
  LexerAdapter(Lexer lexer, Map<TokenType, Integer> tokenTypes,
      int eof) {
    this.lexer = lexer;
    this.tokenTypes = new EnumMap<TokenType, Integer>(tokenTypes);
    this.eof = eof;
  }

  /**
   * Returns a {@linkplain ComplexSymbol} corresponding to the lexer's next
   * token. Returns an EOF token if the lexer has no more tokens.
   * <p>
   * This implementation sets the value of {@link Symbol#value} for each
   * returned {@linkplain Symbol} (except the {@code EOF} symbol) to the result
   * of invoking {@link #valueOf(Token)} on the token returned by the lexer.
   *
   * @return a {@linkplain Symbol} instance corresponding to the lexer's next
   *         token
   * @throws CompilationException
   *           if there is a lexical error
   */
  public final ComplexSymbol next_token() {
    if (!lexer.hasNext()) {
      return new ComplexSymbol(EOF, eof);
    } else {
      Token token = lexer.next();
      PositionAdapter positionAdapter = new PositionAdapter(token.position());
      return new ComplexSymbol(token.type().name(),
          tokenTypes.get(token.type()), positionAdapter.left(),
          positionAdapter.right(), valueOf(token));
    }
  }

  /**
   * Returns the object that will serve as the {@linkplain Symbol#value} for
   * symbols returned by this {@linkplain Scanner}. This value is what CUP
   * provides to actions when you refer to labeled terminals in a CUP
   * specification. For instance, in the following production specification, the
   * value of {@code n} is the value returned by this method:
   *
   * <pre><code> value ::= INTEGER_LITERAL:n {: RESULT = new IntegerNode(n); :}
   * </code></pre>
   *
   * <p>Note that in the above example, the static type of the Java expression
   * {@code n} in the generated code is determined by the type specified in the
   * {@code TERMINAL} declaration for {@code INTEGER_LITERAL} (CUP generates
   * code that performs the appropriate cast).
   *
   * <p>This implementation is the identity: the returned value is the input
   * token. This method may be overridden to provide more convenient
   * functionality (such as returning the literal values for integer, character,
   * and string literal tokens).
   *
   * @param token
   *          the token generated by the {@linkplain Lexer}
   * @return the value to assign to {@linkplain Symbol#value} of the outputted
   *         [{@linkplain Symbol}
   */
  protected Object valueOf(Token token) {
    return token;
  }
}